package com.tedu.aop.demo;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 定义一个切面，用于在某个方法之前或之后或这两者同时动态加入一些逻辑
 *
 * 1.添加@Component注解，该类必须被Spring管理
 * 2.添加@Aspect注解，声明这是一个切面类
 */
@Component
@Aspect
public class LogAspect {

    /*
    @Before注解用于标志当前方法的切入点，实在制定切入点（某方法）之前执行
    该注解需要传入一个参数，用于指定切入点信息(即目标方法)，要描述方法的签名

    切入点描述格式：
    execution(方法签名)

    方法签名格式：public int com.tedu.aop.demo.CalculatorImpl.add(..)
    修饰符 返回值 类的完全限定名.方法名(参数列表)

    其中参数列表可以使用".."，表示任意参数

    其他部分也可以使用通配符"*"
    例如，如果希望在CalculatorImpl任意方法前进行操作可以将方法名用“*”代替
    execution(public int com.tedu.aop.demo.CalculatorImpl.*(..))

     */
    //    @Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(int,int))")
//    @Before("execution(public double com.tedu.aop.demo.CalculatorImpl.add(double,double))")

    //如果方法签名定义与实际方法不一致，则不发生任何作用
//    @Before("execution(public double com.tedu.aop.demo.CalculatorImpl.add(int,double))")//方法签名不一致
//    @Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(double,double))")//返回值类型不一致

    //返回值类型使用*时，可以不明确返回值类型
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(double,double))")
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(..))")

    //如果方法的参数是引用类型时，要写清楚类的完全限定名(包名.类名)
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(java.util.List))")
    //由于String是在java.lang包中，该包默认导入，因此不用写包名
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.add(String))")

//    @Before("execution(public int com.tedu.aop.demo.CalculatorImpl.add(..))")
//    @Before("execution(public int com.tedu.aop.demo.CalculatorImpl.*(..))")

    //无论返回值类型是什么，方法名叫什么，参数列表是什么，只要是CalculatorImpl中的方法，前面都要切入
//    @Before("execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
//    public void before(JoinPoint joinPoint) {
////        Object obj = joinPoint.getTarget();//获取当前切入方法所属对象
////        System.out.println("target:"+obj);
////        Class cls= obj.getClass();//利用反射机制，通过该对象获取其对应类的对象
////        System.out.println(cls.getName());//获取完全限定名（包名.类名）
////        System.out.println(cls.getSimpleName());//仅仅获取类名
////        //通过切入点对象获取方法签名(获取到切入对应方法的信息)
////        Signature signature = joinPoint.getSignature();
////        //通过方法签名对象获取该方法的名字
////        String name = signature.getName();
////        System.out.println("方法："+name);
////        System.out.println("方法信息："+signature);
////
////        //获取参数
////        Object[] args = joinPoint.getArgs();
////        System.out.println("参数："+ Arrays.toString(args));
////        //System.out.println("开始调用add方法");
//        Object o = joinPoint.getTarget();
//        Class cls = o.getClass();
//        String className = cls.getSimpleName();
//        Signature signature = joinPoint.getSignature();
//        String methodName = signature.getName();
//        System.out.println("开始调用"+className+"."+methodName+"()方法");
//    }
//    /**
//     * @AfterReturning注解
//     * 该切入时机是在实际方法调用完毕并且返回值后进行
//     */
//    @AfterReturning(
//            value = "execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))",
//            returning = "result"//用于声明实际方法返回值 传递给当前切面方法的参数
//    )
//    public void afterReturning(JoinPoint joinPoint, Object result){
//        String className = joinPoint.getTarget().getClass().getSimpleName();
//        String methodName = joinPoint.getSignature().getName();
//        System.out.println("调用"+className+"."+methodName+"()方法完毕，返回值为："+result);
//
//
//    }
//    /**
//     * @AfterThrowing
//     * 该注解的切入点是在一个方法执行过程中对外抛出异常后进行。
//     *
//     * 该操作和@AfterReturning只可能有一个。
//     */
//    @AfterThrowing(
//            value = "execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))",
//            throwing = "e")//用于指定实际方法抛出的异常需要传递到当前方法的哪个参数上
//    public void afterThrowing(JoinPoint joinPoint, Exception e){
//        String className = joinPoint.getTarget().getClass().getSimpleName();
//        String methodName = joinPoint.getSignature().getName();
//        System.out.println(className+"."+methodName+"()方法发生异常:"+e.getMessage());
//    }
//    @After("execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
//    public void after(JoinPoint joinPoint){
//        String className = joinPoint.getTarget().getClass().getSimpleName();
//        String methodName = joinPoint.getSignature().getName();
//        System.out.println(className+"."+methodName+"()方法最终执行完毕");
//    }
    @Around("execution(public * com.tedu.aop.demo.CalculatorImpl.*(..))")
    public Object around(ProceedingJoinPoint joinPoint){
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        try {
            //@Before
            System.out.println("开始调用"+className+"."+methodName+"()方法");
            Object returnValue = joinPoint.proceed();
            //@AfterReturning
            System.out.println("调用"+className+"."+methodName+"()方法完毕，返回值为："+returnValue);
            return returnValue;
        }catch (Throwable e){
            //@AfterThrowing
            System.out.println(className+"."+methodName+"()方法发生异常:"+e.getMessage());
            throw new RuntimeException(e);
        }
        finally {
            //@After
            System.out.println(className+"."+methodName+"()方法最终执行完毕");
        }

    }
}
